home *** CD-ROM | disk | FTP | other *** search
/ PCGUIA 127 / PC Guia 127.iso / Software / Utils / Forecastfox / Bin / forecastfox-0.8.5.1-fx+mz+ns.xpi / components / ffProfiles.js < prev    next >
Encoding:
Text File  |  2006-02-18  |  38.0 KB  |  1,209 lines

  1. /* ***** BEGIN LICENSE BLOCK *****
  2.  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
  3.  *
  4.  * The contents of this file are subject to the Mozilla Public License Version
  5.  * 1.1 (the "License"); you may not use this file except in compliance with
  6.  * the License. You may obtain a copy of the License at
  7.  * http://www.mozilla.org/MPL/
  8.  *
  9.  * Software distributed under the License is distributed on an "AS IS" basis,
  10.  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
  11.  * for the specific language governing rights and limitations under the
  12.  * License.
  13.  *
  14.  * The Original Code is Forecastfox.
  15.  *
  16.  * The Initial Developer of the Original Code is
  17.  * Jon Stritar <jstritar@MIT.EDU>.
  18.  * Portions created by the Initial Developer are Copyright (C) 2005
  19.  * the Initial Developer. All Rights Reserved.
  20.  *
  21.  * Contributor(s):
  22.  * Jon Stritar <jstritar@MIT.EDU>
  23.  * Richard Klein <richwklein@mchsi.com>
  24.  *
  25.  * Alternatively, the contents of this file may be used under the terms of
  26.  * either the GNU General Public License Version 2 or later (the "GPL"), or
  27.  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
  28.  * in which case the provisions of the GPL or the LGPL are applicable instead
  29.  * of those above. If you wish to allow use of your version of this file only
  30.  * under the terms of either the GPL or the LGPL, and not to allow others to
  31.  * use your version of this file under the terms of the MPL, indicate your
  32.  * decision by deleting the provisions above and replace them with the notice
  33.  * and other provisions required by the GPL or the LGPL. If you do not delete
  34.  * the provisions above, a recipient may use your version of this file under
  35.  * the terms of any one of the MPL, the GPL or the LGPL.
  36.  *
  37.  * ***** END LICENSE BLOCK ***** */
  38.  
  39. /* XPCOM Constants */
  40. const CLASS_ID = Components.ID("{27e1bca0-aded-11d9-9669-0800200c9a66}");
  41. const CLASS_NAME = "Forecastfox Profiles Component";
  42. const CONTRACT_ID = "@ensolis.com/forecastfox/profiles;1";
  43. const nsIDOMXPathResult = Components.interfaces.nsIDOMXPathResult;
  44. const nsIFilePicker = Components.interfaces.nsIFilePicker;
  45. const ffIError = Components.interfaces.ffIError;
  46. const ffIProfiles = Components.interfaces.ffIProfiles;
  47. const ffIDisk = Components.interfaces.ffIDisk;
  48.  
  49. /******************************************************************************
  50.  * ffProfiles Component
  51.  ******************************************************************************/
  52. function ffProfiles() { };
  53.  
  54. ffProfiles.prototype = {
  55.   _batch: false,
  56.   _current: null,
  57.   _doc: null,
  58.   _manager: null,  
  59.   _loading: false,
  60.   _rotating: false,    
  61.   _prefs: null,  
  62.   _xpath: null,
  63.   _xresolve: null,
  64.   _obs: null,
  65.   _default: null,
  66.   _timer: null,
  67.   _bundle: null,
  68.   
  69.   get batch() { return this._batch; },
  70.   
  71.   get current() { 
  72.     if (!this._current)
  73.       return "";
  74.       
  75.     return this._current;  
  76.   },
  77.   
  78.   get document() { return this._doc },
  79.   set document(aValue) {
  80.     this._doc = aValue;
  81.     var file = this._manager.disk.get("profiles.xml", ffIDisk.TYPE_CACHE);    
  82.     this._manager.disk.write(this._doc, file);
  83.   },
  84.   
  85.   start: function()
  86.   {
  87.     this._loading = true;
  88.       
  89.     //set up components
  90.     this._xpath = Components.classes["@mozilla.org/dom/xpath-evaluator;1"].getService(Components.interfaces.nsIDOMXPathEvaluator);
  91.     this._xresolve = Components.classes["@ensolis.com/forecastfox/resolver;1"].createInstance(Components.interfaces.nsIDOMXPathNSResolver);
  92.     this._obs = Components.classes["@mozilla.org/observer-service;1"].getService(Components.interfaces.nsIObserverService);
  93.     this._manager = Components.classes["@ensolis.com/forecastfox/manager;1"].getService(Components.interfaces.ffIManager);
  94.     this._timer = Components.classes["@mozilla.org/timer;1"].createInstance(Components.interfaces.nsITimer);
  95.     var sbs = Components.classes["@mozilla.org/intl/stringbundle;1"].getService(Components.interfaces.nsIStringBundleService);    
  96.     this._bundle = sbs.createBundle("chrome://forecastfox/locale/forecastfox.properties");    
  97.     var pbs = Components.classes["@mozilla.org/preferences-service;1"].getService(Components.interfaces.nsIPrefService);
  98.     this._default = pbs.getDefaultBranch("forecastfox.");
  99.     
  100.     //load preference names from the default file
  101.     this._prefs = this._getPrefs();
  102.     
  103.     //load and migrate data
  104.     var error = this._loadDoc();
  105.     
  106.     //start rotating
  107.     if (error.severity != ffIError.SEVERITY_ERROR)
  108.       this.rotate(ffIProfiles.ROTATE_START); 
  109.    
  110.     this._loading = false;
  111.     return error;
  112.   },
  113.   
  114.   stop: function()
  115.   {  
  116.     //save the current profile
  117.     this.save(this._current);
  118.     
  119.     //cancel timer
  120.     this._timer.cancel();
  121.     
  122.     //destroy variables   
  123.     this._batch = null;
  124.     this._current = null;
  125.     this._doc = null;
  126.     this._manager = null;
  127.     this._loading = null;
  128.     this._rotating = null;        
  129.     this._prefs = null;
  130.     this._xpath = null;
  131.     this._xresolve = null;
  132.     this._obs = null;
  133.     this._default = null;
  134.     this._timer = null;
  135.     this._bundle = null;
  136.   },
  137.   
  138.   load: function(aName)
  139.   {
  140.     this._batch = true;
  141.     this.rotate(ffIProfiles.ROTATE_STOP);
  142.     this._manager.cancel();  
  143.       
  144.     //save the current profile
  145.     this.save(this._current, true);
  146.  
  147.     //get profile
  148.     var profiles = this._evalPath(this._doc, "//profiles");
  149.     var profile = this._evalPath(profiles, "./profile[@id='" + aName + "']");
  150.  
  151.     //load preferences
  152.     for (var x=0; x<this._prefs.length; x++) {
  153.       var pref = this._evalPath(profile, "./pref[@name='" + this._prefs[x] + "']");
  154.       this._setPref(pref);
  155.     };
  156.     
  157.     //set current
  158.     this._current = aName;   
  159.     
  160.     //notify complete
  161.     this._batch = false;
  162.     if (!this._loading) {
  163.       this._obs.notifyObservers(this, "forecastfox:profile", aName);
  164.       this.rotate(ffIProfiles.ROTATE_START);          
  165.     }
  166.   },
  167.   
  168.   save: function(aName, aTest)
  169.   {
  170.     //return if nothing passed
  171.     if (!aName)
  172.       return;
  173.     
  174.     //test if a valid profile
  175.     if (!this._isProfile(aName) && aTest)
  176.       return;
  177.         
  178.     var profiles = this._evalPath(this._doc, "//profiles");
  179.     var profile = this._evalPath(profiles, "./profile[@id='" + aName + "']");
  180.     
  181.     //check if this is a new profile
  182.     if (profile)
  183.       profile.parentNode.removeChild(profile);
  184.  
  185.     //create profile
  186.     profile = this._doc.createElement("profile");
  187.     profile.setAttribute("id", aName);
  188.     
  189.     //add preference to it
  190.     var pref, type;
  191.     for (var x=0; x<this._prefs.length; x++) {
  192.       type = this._getPrefType(this._prefs[x], false);
  193.       pref = this._doc.createElement("pref");
  194.       pref.setAttribute("name", this._prefs[x]);
  195.       pref.setAttribute("type", type);
  196.       pref.setAttribute("value", eval("this._manager.branch.get" + type + "Pref('" + this._prefs[x] + "')"));
  197.       profile.appendChild(pref);
  198.     };
  199.     
  200.     //append profile to profiles
  201.     profiles.appendChild(profile);
  202.  
  203.     //flush to disk
  204.     var file = this._manager.disk.get("profiles.xml", ffIDisk.TYPE_CACHE);
  205.     this._manager.disk.write(this._doc, file, true);
  206.   },
  207.  
  208.   rotate: function(aRotate)
  209.   {
  210.     //cancel in timers
  211.     this._timer.cancel();
  212.  
  213.     //stop rotating    
  214.     if (aRotate == ffIProfiles.ROTATE_STOP)
  215.       this._rotating = false;
  216.     else {
  217.       this._rotating = true;
  218.       this._schedule();
  219.     }
  220.   },
  221.  
  222.   create: function(aName)
  223.   {
  224.     this._batch = true;
  225.     this.rotate(ffIProfiles.ROTATE_STOP);
  226.     this._manager.cancel();
  227.     
  228.     //make sure profile is saved
  229.     this.save(this._current, true);
  230.  
  231.     //clone the current profile
  232.     var name = this._stripName(aName);
  233.     var profiles = this._evalPath(this._doc, "//profiles");
  234.     var profile = this._evalPath(profiles, "./profile[@id='" + this._current + "']");
  235.     if (!profile)
  236.       profile = this._doc.createElement("profile");
  237.     var to = profile.cloneNode(true);
  238.             
  239.     //set id
  240.     to.setAttribute("id", name);
  241.         
  242.     //copy cache
  243.     this._cacheFiles(profile, to, false);
  244.     this._cachePrefs(profile, to);
  245.     
  246.     //append
  247.     profiles.appendChild(to);
  248.         
  249.     //set current pref which will
  250.     //load this one and flush data to disk
  251.     this._batch = false;
  252.     this.rotate(ffIProfiles.ROTATE_START);    
  253.     this._manager.setUTFPref("profile.current", name);       
  254.   },
  255.   
  256.   rename: function(aName, aTo)
  257.   {
  258.     this._batch = true;
  259.     this.rotate(ffIProfiles.ROTATE_STOP);
  260.     this._manager.cancel();   
  261.     
  262.     //make sure profile is saved
  263.     this.save(this._current, true);
  264.  
  265.     //clone the from profile
  266.     var name = this._stripName(aTo);
  267.     var profiles = this._evalPath(this._doc, "//profiles");
  268.     var profile = this._evalPath(profiles, "./profile[@id='" + aName + "']");
  269.     var to = profile.cloneNode(true);
  270.    
  271.     //set id
  272.     to.setAttribute("id", name);
  273.         
  274.     //copy cache
  275.     this._cacheFiles(profile, to, true);
  276.     this._cachePrefs(profile, to);
  277.     
  278.     //append
  279.     profiles.appendChild(to); 
  280.     
  281.     //remove from
  282.     profiles.removeChild(profile);
  283.     
  284.     //set current pref which will
  285.     //load this one and flush data to disk
  286.     this._current = null;  
  287.     this._batch = false;
  288.     this.rotate(ffIProfiles.ROTATE_START);        
  289.     this._manager.setUTFPref("profile.current", name);
  290.   },
  291.   
  292.   remove: function(aName, aTo)
  293.   {
  294.     this._batch = true;
  295.     this.rotate(ffIProfiles.ROTATE_STOP);
  296.     this._manager.cancel();   
  297.           
  298.     //make sure profile is saved
  299.     this.save(this._current, true);
  300.       
  301.     //get the from profile
  302.     var profiles = this._evalPath(this._doc, "//profiles");
  303.     var profile = this._evalPath(profiles, "./profile[@id='" + aName + "']");
  304.     
  305.     //delete cache files
  306.     this._cacheFiles(profile, null, true);
  307.     
  308.     //remove from
  309.     profiles.removeChild(profile);
  310.     
  311.     //set current pref which will
  312.     //load this one and flush data to disk
  313.     this._current = null;
  314.     this._batch = false;
  315.     this.rotate(ffIProfiles.ROTATE_START);        
  316.     this._manager.setUTFPref("profile.current", aTo);   
  317.   },
  318.   
  319.   getProfileNum: function()
  320.   {
  321.     var profiles = this._evalPath(this._doc, "//profiles");
  322.     profiles = profiles.getElementsByTagName("profile");
  323.     return profiles.length;
  324.   },
  325.   
  326.   getProfile: function(aIndex)
  327.   {
  328.     var profiles = this._evalPath(this._doc, "//profiles");
  329.     var val = new Array();
  330.     profiles = profiles.getElementsByTagName("profile");
  331.     return profiles[aIndex].getAttribute("id");
  332.   },
  333.   
  334.   open: function(aOpen, aWindow)
  335.   {
  336.     //set variables
  337.     var alerts = Components.classes["@mozilla.org/embedcomp/prompt-service;1"].getService(Components.interfaces.nsIPromptService);
  338.         
  339.     //get file
  340.     var file = this._filePicker(aOpen, aWindow);
  341.     if (!file)
  342.       return;
  343.     
  344.     //do import-export  
  345.     switch (aOpen) {
  346.       case ffIProfiles.OPEN_EXPORT:
  347.         this._export(alerts, aWindow, file);
  348.         break;
  349.       case ffIProfiles.OPEN_IMPORT:
  350.         this._import(alerts, aWindow, file);
  351.         break;
  352.     }
  353.   },
  354.      
  355.   _loadDoc: function()
  356.   {
  357.     this._batch = true;
  358.     var from = this._manager.branch.getCharPref("migrated");
  359.     var previous = this._manager.branch.prefHasUserValue("migrated");
  360.     var file = this._manager.disk.get("profiles.xml", ffIDisk.TYPE_CACHE);
  361.     var exists = file.exists();
  362.  
  363.     //do simple load
  364.     if (previous)
  365.       return this._simpleLoad(from, file);
  366.   
  367.     //do complex load
  368.     return this._complexLoad(from, file, exists);
  369.   },
  370.   
  371.   _simpleLoad: function(aFrom, aFile)
  372.   {    
  373.     // completes the uninstallation/upgrade of weatherfox
  374.     if (aFrom == "*") {
  375.       var pbs = Components.classes["@mozilla.org/preferences-service;1"].getService(Components.interfaces.nsIPrefService)
  376.       var branch = pbs.getBranch("weatherfox.");
  377.       branch.deleteBranch("");
  378.       this._manager.disk.remove(ffIDisk.TYPE_WEATHERFOX);      
  379.     };
  380.       
  381.     //get profile document
  382.     var error = this._getDoc(aFile);
  383.     if (error.severity != ffIError.SEVERITY_INFO)
  384.       return error;
  385.     if (!this._doc)
  386.       return this._createError(ffIError.SEVERITY_ERROR, "ff.migrate", null, null);
  387.             
  388.     //determine from version
  389.     var from = this._evalPath(this._doc, "//@version");
  390.     if (from) 
  391.       from = from.textContent;
  392.     else
  393.       from = aFrom;
  394.       
  395.     //do transforms
  396.     var doc = this._transform(this._doc, from);
  397.     if (!doc) {
  398.       this._doc = null;
  399.       return this._createError(ffIError.SEVERITY_ERROR, "ff.transform", null, null);
  400.     } else
  401.       this._doc = doc;
  402.  
  403.     //clear icons if before 0.8.5
  404.     try {
  405.       var vchecker = Components.classes["@mozilla.org/updates/version-checker;1"].getService(Components.interfaces.nsIVersionChecker);        
  406.       var compare = vchecker.compare(from, "0.8.5");    
  407.     } catch(e) {
  408.       try {
  409.         vchecker = Components.classes["@mozilla.org/xpcom/version-comparator;1"].getService(Components.interfaces.nsIVersionComparator); 
  410.       } catch(e) {
  411.         vchecker = null;
  412.       };     
  413.       compare = (from < "0.8.5") ? -1 : 0;
  414.     };
  415.     if (compare < 0)
  416.       this._manager.disk.clear(ffIDisk.TYPE_ICONS, false);
  417.                             
  418.     //finalize migration
  419.     this._finishLoad(aFile, "0.8.5");
  420.     return this._createError(ffIError.SEVERITY_INFO, null, "", "");      
  421.   },
  422.   
  423.   _complexLoad: function(aFrom, aFile, aExists)
  424.   {
  425.     //determine if we are in toolkit
  426.     var isToolkit = true;
  427.     try {
  428.       var em = Components.classes["@mozilla.org/extensions/manager;1"].getService(Components.interfaces.nsIExtensionManager);
  429.     } catch (e) {
  430.        isToolkit = false;
  431.     };
  432.  
  433.     //non toolkit version
  434.     if (!isToolkit)
  435.       return this._nonToolkitLoad(aFrom, aFile, aExists);
  436.  
  437.     //toolkit version     
  438.     //determine if we are coming from weatherfox
  439.     var isWeatherfox = false;
  440.     var weatherfox = new Array();
  441.     try {
  442.       weatherfox[0] = em.getItemForID("{BA8E053E-2867-4772-B9F0-26A5979AFA09}");
  443.       isWeatherfox = (weatherfox[0].location != null);
  444.     } catch(e) {
  445.       weatherfox = em.getItemList("{BA8E053E-2867-4772-B9F0-26A5979AFA09}", Components.interfaces.nsIUpdateItem.TYPE_EXTENSION, {});
  446.       isWeatherfox = (weatherfox[0] != null);
  447.     };
  448.         
  449.     //not weatherfox
  450.     if (!isWeatherfox)
  451.       return this._toolkitLoad(aFrom, aFile, aExists);
  452.  
  453.     //is weatherfox
  454.     return this._weatherfoxLoad(aFrom, aFile, aExists, em, weatherfox);
  455.   },
  456.   
  457.   _nonToolkitLoad: function(aFrom, aFile, aExists)
  458.   {
  459.     //get profile document
  460.     var error = this._getDoc(aFile);
  461.     if (error.severity != ffIError.SEVERITY_INFO)
  462.       return error;
  463.     if (!this._doc)
  464.       return this._createError(ffIError.SEVERITY_WARNING, "ff.migrate", null, null);
  465.         
  466.     // bug in version 0.7.0 and 0.7.1 for non toolkit did not update migrate pref
  467.     // so manually override it here if its null
  468.     var doc;
  469.     if (aExists && aFrom == "")        
  470.       doc = this._transform(this._doc, "0.7.1");
  471.     else
  472.       doc = this._doc;
  473.     if (!doc) {
  474.       this._doc = null;
  475.       return this._createError(ffIError.SEVERITY_ERROR, "ff.transform", null, null);
  476.     } else
  477.       this._doc = doc;
  478.             
  479.     //finalize migration
  480.     this._finishLoad(aFile, "0.8.5");
  481.     return this._createError(ffIError.SEVERITY_INFO, null, "", "");       
  482.   },
  483.   
  484.   _toolkitLoad: function(aFrom, aFile, aExists)
  485.   {
  486.     //get profile document
  487.     var error = this._getDoc(aFile);
  488.     if (error.severity != ffIError.SEVERITY_INFO)
  489.       return error;
  490.     if (!this._doc)
  491.       return this._createError(ffIError.SEVERITY_WARNING, "ff.migrate", null, null);
  492.             
  493.     //has to be either 0.5.8 or 0.5.9 
  494.     //so error on lower side or doesn't exist
  495.     var doc;
  496.     if (aExists)
  497.       doc = this._transform(this._doc, "0.5.8");
  498.     else
  499.       doc = this._doc;                 
  500.     if (!doc) {
  501.       this._doc = null;
  502.       return this._createError(ffIError.SEVERITY_ERROR, "ff.transform", null, null);
  503.     } else
  504.       this._doc = doc;
  505.             
  506.     //finalize migration
  507.     this._finishLoad(aFile, "0.8.5");
  508.     return this._createError(ffIError.SEVERITY_INFO, null, "", "");       
  509.   },
  510.   
  511.   _weatherfoxLoad: function(aFrom, aFile, aExists, aEM, aWeatherfox)
  512.   {
  513.     //get profile document
  514.     var error = this._getDoc(aFile);
  515.     if (error.severity != ffIError.SEVERITY_INFO)
  516.       return error;
  517.     if (!this._doc)
  518.       return this._createError(ffIError.SEVERITY_WARNING, "ff.migrate", null, null);
  519.             
  520.     //can't transform a version before 0.4.8
  521.     try {
  522.       var vchecker = Components.classes["@mozilla.org/updates/version-checker;1"].getService(Components.interfaces.nsIVersionChecker);        
  523.     } catch(e) {
  524.       vchecker = Components.classes["@mozilla.org/xpcom/version-comparator;1"].getService(Components.interfaces.nsIVersionComparator);    
  525.     };
  526.     
  527.     if (vchecker.compare(aWeatherfox[0].version, "0.4.8") < 0)
  528.       return this._createError(ffIError.SEVERITY_ERROR, "ff.migrate.version", null, null);
  529.     
  530.     //transform the weatherfox profile
  531.     var wfile = this._manager.disk.get("profiles.xml", ffIDisk.TYPE_WEATHERFOX);
  532.     if (wfile.exists() && wfile.isReadable()) {
  533.       var wDoc = this._manager.disk.read(wfile);
  534.       if (this._manager.disk.valid(wDoc)) {
  535.         var doc = this._transform(wDoc, aWeatherfox[0].version);
  536.         if (!doc)
  537.           return this._createError(ffIError.SEVERITY_ERROR, "ff.transform", null, null);
  538.         else
  539.           this._doc = doc;
  540.           
  541.         //set current profile
  542.         var pbs = Components.classes["@mozilla.org/preferences-service;1"].getService(Components.interfaces.nsIPrefService)
  543.         var branch = pbs.getBranch("weatherfox.");
  544.         this._manager.setUTFPref("profile.current", branch.getCharPref("profile"));
  545.       };          
  546.     };        
  547.          
  548.     //remove weatherfox from em 
  549.     try {
  550.       // use this 1.1 and on
  551.       aEM.uninstallItem("{BA8E053E-2867-4772-B9F0-26A5979AFA09}");
  552.     } catch (e) {
  553.       // use this through 1.0
  554.       aEM.uninstallExtension("{BA8E053E-2867-4772-B9F0-26A5979AFA09}");
  555.     };
  556.             
  557.     //finalize migration
  558.     this._finishLoad(aFile, "*");
  559.     if (error.severity != ffIError.SEVERITY_ERROR)                    
  560.       return this._createError(ffIError.SEVERITY_WARNING, "ff.migrate.restart", null, null);
  561.     else
  562.       return error;    
  563.   },
  564.   
  565.   _finishLoad: function(aFile, aVersion)
  566.   {
  567.     this._manager.disk.write(this._doc, aFile, true);
  568.     this._manager.branch.setCharPref("migrated", aVersion); 
  569.  
  570.     //XXX seems like there may be some redundancy in here, there
  571.     //  could there be any synchronization problems too?
  572.     var profile;    
  573.     if (!this._isProfile(this._manager.getUTFPref("profile.current"))) {
  574.       profile = this._manager.getUTFPref("profile.current");
  575.       this.create(profile);
  576.     };
  577.     profile = this._firstProfile();
  578.     
  579.     //complete migration by setting the migrated prefs
  580.     var profiles = this._evalPath(this._doc, "//profiles");
  581.     var profileNode = this._evalPath(profiles, "./profile[@id='" + profile + "']");
  582.     
  583.     for (var x=0; x<this._prefs.length; x++) {
  584.       var pref = this._evalPath(profile, "./pref[@name='" + this._prefs[x] + "']");
  585.       this._setPref(pref);
  586.     };
  587.     
  588.     this._current = profile;
  589.     
  590.     this._batch = false;
  591.   },
  592.   
  593.   _getDoc: function(aFile)
  594.   {
  595.     var backup = this._manager.disk.get("profiles.bak", ffIDisk.TYPE_CACHE);
  596.     var doc = null;
  597.     
  598.     //try loading profile file first
  599.     if (aFile.exists()) {
  600.       if (!aFile.isReadable() || !aFile.isWritable()) {
  601.         this._doc = null;
  602.         var err_tool = this._bundle.formatStringFromName("ff.migrate.read.tooltip", [aFile.path], 1);
  603.         var err_label = this._bundle.GetStringFromName("ff.migrate.read.label");
  604.         return this._createError(ffIError.SEVERITY_ERROR, null, err_label, err_tool);
  605.       };
  606.       doc = this._manager.disk.read(aFile);
  607.       if (this._manager.disk.valid(doc)) {
  608.         this._doc = doc;
  609.         return this._createError(ffIError.SEVERITY_INFO, null, "", "");
  610.       } else
  611.         this._manager.disk.recordFile(aFile);
  612.     } else
  613.       this._manager.disk.recordMessage("Message: Expected profiles.xml, but was missing.");
  614.     
  615.     //try loading backup
  616.     if (backup.exists() && backup.isReadable()) {    
  617.       doc = this._manager.disk.read(backup);
  618.       if (this._manager.disk.valid(doc)) {
  619.         this._doc = doc;
  620.         return this._createError(ffIError.SEVERITY_INFO, null, "", "");
  621.       } else
  622.         this._manager.disk.recordFile(backup);
  623.     } else
  624.       this._manager.disk.recordMessage("Message: Expected backup profiles.xml, but was missing.");
  625.     
  626.     //create default profiles document
  627.     doc = this._createDefault();
  628.     this._manager.disk.clear(ffIDisk.TYPE_CACHE, true);
  629.     this._doc = doc;      
  630.     return this._createError(ffIError.SEVERITY_INFO, "ff.migrate.default", null, null);        
  631.   },
  632.   
  633.   _createDefault: function()
  634.   {
  635.     //create empty document
  636.     var contents = new String();
  637.     var parser = Components.classes["@mozilla.org/xmlextras/domparser;1"].createInstance(Components.interfaces.nsIDOMParser);           
  638.     contents += '<?xml version="1.0"?>\n' + 
  639.                 '<profiles version="0.8.5">\n' +
  640.                 '</profiles>\n'
  641.     var doc = parser.parseFromString(contents, "text/xml");
  642.     
  643.     //create default profile node
  644.     var profiles = doc.getElementsByTagName("profiles")[0];
  645.     var profile = doc.createElement("profile");
  646.     profile.setAttribute("id", "default");
  647.     profiles.appendChild(profile);
  648.         
  649.     //add preferences
  650.     var pref, type;
  651.     for (var x=0; x<this._prefs.length; x++) {
  652.       type = this._getPrefType(this._prefs[x], true);
  653.       pref = doc.createElement("pref");
  654.       pref.setAttribute("name", this._prefs[x]);
  655.       pref.setAttribute("type", type);
  656.       try {
  657.         pref.setAttribute("value", eval("this._default.get" + type + "Pref('" + this._prefs[x] + "')"));
  658.       } catch(e) {
  659.         switch (type) {
  660.           case "Char":
  661.             pref.setAttribute("value", "");
  662.             break;
  663.           case "Int":
  664.             pref.setAttribute("value", "0");
  665.             break;
  666.           case "Bool":
  667.             pref.setAttribute("value", "false");
  668.             break;
  669.         };
  670.       };
  671.       profile.appendChild(pref);
  672.     };
  673.         
  674.     //return created document  
  675.     return doc;
  676.   },
  677.    
  678.   _transform: function(aDoc, aFrom)
  679.   {
  680.     var processor = Components.classes["@mozilla.org/document-transformer;1?type=xslt"].createInstance(Components.interfaces.nsIXSLTProcessor);  
  681.     var from = aFrom;
  682.     
  683.     //use version checker if possible
  684.     try {
  685.       var vchecker = Components.classes["@mozilla.org/updates/version-checker;1"].getService(Components.interfaces.nsIVersionChecker);        
  686.       var compare = vchecker.compare(from, "0.8.5");    
  687.     } catch(e) {
  688.       try {
  689.         vchecker = Components.classes["@mozilla.org/xpcom/version-comparator;1"].getService(Components.interfaces.nsIVersionComparator); 
  690.       } catch(e) {
  691.         vchecker = null;
  692.       };     
  693.       compare = (from < "0.8.5") ? -1 : 0;
  694.     };
  695.                 
  696.     //loop up through version
  697.     while (compare < 0) {
  698.  
  699.       //invalid document
  700.       if (!this._manager.disk.valid(aDoc)) {
  701.         return null;
  702.       };
  703.     
  704.       //load the stylesheet
  705.       var sheet = this._manager.disk.get("profiles-" + from + ".xsl", ffIDisk.TYPE_TRANSFORMS);
  706.       if (!sheet.exists() || !sheet.isReadable()) {
  707.         return null;
  708.       };
  709.       
  710.       //invalid stylesheet         
  711.       var sdoc = this._manager.disk.read(sheet);
  712.       if (!this._manager.disk.valid(sdoc)) {
  713.         return null;
  714.       };
  715.     
  716.       //transform document
  717.       processor.reset();
  718.       processor.importStylesheet(sdoc);
  719.       var frag = processor.transformToFragment(aDoc.getElementsByTagName("profiles")[0], aDoc);
  720.       aDoc.replaceChild(frag.firstChild, aDoc.getElementsByTagName("profiles")[0]);
  721.            
  722.       //get version doc was transformed to
  723.       var node = this._evalPath(aDoc, "//@version");
  724.       if (!node) {
  725.         return null;
  726.       } else
  727.         from = node.textContent;
  728.         
  729.       if (vchecker) {
  730.         compare = vchecker.compare(from, "0.8.5");    
  731.       } else {
  732.         compare = (from < "0.8.5") ? -1 : 0;
  733.       };  
  734.     };
  735.     
  736.     return aDoc;
  737.   },
  738.   
  739.   _import: function(aAlerts, aWindow, aFile)
  740.   { 
  741.     //load import file
  742.     if (!aFile.exists()) {
  743.       aAlerts.alert(aWindow, this._bundle.GetStringFromName("ff.import"), this._bundle.GetStringFromName("ff.import.document"));
  744.       return;
  745.     };   
  746.     if (!aFile.isReadable()) {
  747.       aAlerts.alert(aWindow, this._bundle.GetStringFromName("ff.import"), this._bundle.GetStringFromName("ff.import.read"));
  748.       return;
  749.     };
  750.     var doc = this._manager.disk.read(aFile);
  751.     if (!this._manager.disk.valid(doc)) {
  752.       aAlerts.alert(aWindow, this._bundle.GetStringFromName("ff.import"), this._bundle.GetStringFromName("ff.import.document"));
  753.       return;
  754.     };
  755.        
  756.     //have current and profiles
  757.     var profiles = doc.getElementsByTagName("profiles")[0];
  758.     if (!profiles) {
  759.       aAlerts.alert(aWindow, this._bundle.GetStringFromName("ff.import"), this._bundle.GetStringFromName("ff.import.invalid"));
  760.       return;
  761.     };
  762.         
  763.     //get version we are importing from
  764.     var version = this._evalPath(doc, "//@version");
  765.     if (!version) {
  766.       aAlerts.alert(aWindow, this._bundle.GetStringFromName("ff.import"), this._bundle.GetStringFromName("ff.import.invalid"));
  767.       return;
  768.     } else
  769.       version = version.textContent;
  770.      
  771.     //get profiles node in current doc
  772.     //and replace with profiles node in import doc
  773.     var cDoc = this._doc.cloneNode(true);
  774.     var node = this._evalPath(cDoc, "//profiles");
  775.     var parent = node.parentNode;
  776.     parent.removeChild(node);
  777.     parent.appendChild(profiles.cloneNode(true));
  778.         
  779.     //tranform document
  780.     cDoc = this._transform(cDoc, version);
  781.     if (!cDoc) {
  782.       aAlerts.alert(aWindow, this._bundle.GetStringFromName("ff.import"), this._bundle.GetStringFromName("ff.transform.tooltip"));      
  783.       return;                  
  784.     };
  785.         
  786.     //flush data to disk
  787.     var file = this._manager.disk.get("profiles.xml", ffIDisk.TYPE_CACHE);
  788.     this._manager.disk.write(cDoc, file, true); 
  789.         
  790.     //load profile
  791.     this._current = null;
  792.     this._doc = cDoc; 
  793.     
  794.     if (this._manager.getUTFPref("profile.current") == this._firstProfile())
  795.       this.load(this._firstProfile());
  796.     else
  797.       this._manager.setUTFPref("profile.current", this._firstProfile());
  798.     
  799.     aAlerts.alert(aWindow, this._bundle.GetStringFromName("ff.import"), this._bundle.GetStringFromName("ff.import.success"));      
  800.   },
  801.   
  802.   _export: function(aAlerts, aWindow, aFile)
  803.   {
  804.     //create content string of file
  805.     var contents = new String();
  806.     contents += '<?xml version="1.0"?>\n' + 
  807.                 '<forecastfox-settings>\n' +
  808.                 '</forecastfox-settings>\n'
  809.     
  810.     //convert to dom document
  811.     var parser = Components.classes["@mozilla.org/xmlextras/domparser;1"].createInstance(Components.interfaces.nsIDOMParser);       
  812.     var doc = parser.parseFromString(contents, "text/xml");
  813.     
  814.     //copy profiles to export document
  815.     var profiles = this._evalPath(this._doc, "//profiles");
  816.     var node = doc.getElementsByTagName("forecastfox-settings")[0];
  817.     node.appendChild(profiles.cloneNode(true));
  818.         
  819.     //flush to disk
  820.     if (aFile.exists() && !aFile.isWritable()) {
  821.       aAlerts.alert(aWindow, this._bundle.GetStringFromName("ff.export"), this._bundle.GetStringFromName("ff.export.write"));
  822.       return;
  823.     };
  824.     
  825.     //finished    
  826.     this._manager.disk.write(doc, aFile, false);
  827.     aAlerts.alert(aWindow, this._bundle.GetStringFromName("ff.export"), this._bundle.GetStringFromName("ff.export.success"));
  828.   },
  829.   
  830.   _firstProfile: function()
  831.   {    
  832.     //current profile set and valid
  833.     var profile = this._current;
  834.     if (profile && this._isProfile(profile))
  835.       return profile;
  836.     
  837.     //preference set and valid
  838.     profile = this._manager.getUTFPref("profile.current");
  839.     if (profile && this._isProfile(profile))
  840.       return profile;
  841.       
  842.     //default profile exists
  843.     profile = "default";
  844.     if (profile && this._isProfile(profile))
  845.       return profile;
  846.     
  847.     //grab first profile available
  848.     profile = this._doc.getElementsByTagName("profile")[0].getAttribute("id");
  849.     if (profile && this._isProfile(profile))
  850.       return profile;
  851.       
  852.     return null;
  853.   },
  854.       
  855.   _createError: function(aSeverity, aName, aDescription, aTooltip)
  856.   {
  857.     var description = (aName) ? this._bundle.GetStringFromName(aName + ".label") : aDescription;
  858.     var tooltip = (aName) ? this._bundle.GetStringFromName(aName + ".tooltip") : aTooltip;
  859.     var error = Components.classes["@ensolis.com/forecastfox/error;1"].createInstance(Components.interfaces.ffIError);
  860.     error.init(aSeverity, "@ensolis.com/forecastfox/profiles;1", description, tooltip); 
  861.     return error;
  862.   },
  863.  
  864.   _filePicker: function(aOpen, aWindow)
  865.   {
  866.     var picker = Components.classes["@mozilla.org/filepicker;1"].createInstance(nsIFilePicker);  
  867.     picker.appendFilters(nsIFilePicker.filterXML);
  868.     picker.defaultExtension = ".xml";
  869.     switch (aOpen) {
  870.       case ffIProfiles.OPEN_EXPORT:
  871.         picker.init(aWindow, this._bundle.GetStringFromName("ff.export"), nsIFilePicker.modeSave);
  872.         break;
  873.       case ffIProfiles.OPEN_IMPORT:
  874.         picker.init(aWindow, this._bundle.GetStringFromName("ff.import"), nsIFilePicker.modeOpen);
  875.         break;
  876.     };
  877.     
  878.     // get the file and its contents
  879.     var res = picker.show();
  880.     if (res == nsIFilePicker.returnCanel)
  881.       return null;
  882.     else
  883.       return picker.file;
  884.   },
  885.   
  886.   _isProfile: function(aName)
  887.   {
  888.     var profile = this._evalPath(this._doc, "//profiles/profile[@id='"+aName+"']");
  889.     return (profile != null);
  890.   },
  891.   
  892.   _schedule: function()
  893.   {
  894.     if (!this._rotating)
  895.       this.rotate(ffIProfiles.ROTATE_STOP);
  896.     else {
  897.       //see if we have multiple profiles
  898.       var profiles = this._evalPath(this._doc, "//profiles");
  899.       if (profiles.getElementsByTagName("profile").length <= 1)
  900.         return;
  901.  
  902.       var rotate = this._manager.branch.getBoolPref("profile.switch.enabled");
  903.       var rotateTime = this._manager.branch.getIntPref("profile.switch.delay");
  904.     
  905.       //bail out if its disabled
  906.       if (rotate == false || rotateTime <= 0)
  907.         return;
  908.  
  909.       rotateTime *= 1000 * 60;
  910.  
  911.       // then schedule the timer
  912.       this._timer.initWithCallback(this, rotateTime, Components.interfaces.nsITimer.TYPE_ONE_SHOT);
  913.     }     
  914.   },
  915.   
  916.   _encodeValue: function(aValue)
  917.   {
  918.     // only encode the strings
  919.     if (typeof aValue != "string")
  920.       return aValue;
  921.     
  922.     // prepare the quotes so that we can use eval
  923.     return aValue.replace(/"/g,"\\\"").replace(/'/g,"\\\'");
  924.   },
  925.     
  926.   _setPref: function(aNode)
  927.   {
  928.     if (!aNode)
  929.       return;
  930.       
  931.     //get variables
  932.     var pref  = aNode.getAttribute("name");
  933.     var type  = aNode.getAttribute("type");
  934.     var value = aNode.getAttribute("value");
  935.     
  936.     //set value based on type
  937.     if (type == "Bool")
  938.       value = (value == "true") ? true : false;
  939.     else if (type == "Int")
  940.       value = Number(value);
  941.     else if (type == "Char")
  942.       value = value
  943.  
  944.     //get the old value
  945.     var oldValue = null;
  946.     var defaultValue = null;
  947.     var hasValue = false;
  948.     
  949.     try {
  950.       oldValue = eval("this._manager.branch.get" + type + "Pref('" + pref + "')");
  951.       defaultValue = eval("this._default.get" + type + "Pref('" + pref + "')");
  952.       hasValue = eval("this._manager.branch.prefHasUserValue('" + pref + "')");
  953.     } catch(e) {};
  954.     
  955.     //do nothing if the same value
  956.     if (value == oldValue)
  957.       return;
  958.     
  959.     //clear if same as default value
  960.     if (value == defaultValue && hasValue)
  961.       eval("this._manager.branch.clearUserPref('" + pref + "')");
  962.     else {
  963.       if (type == "Char")
  964.         value = '"' + this._encodeValue(value) + '"'; 
  965.       eval("this._manager.branch.set" + type + "Pref('" + pref + "', " + value + ")");
  966.     }
  967.   },
  968.     
  969.   _getPrefType: function(aName, aDefault)
  970.   {
  971.     //convert integer type to character type
  972.     var type;
  973.     if (!aDefault)
  974.       type = this._manager.branch.getPrefType(aName);
  975.     else
  976.       type = this._default.getPrefType(aName);
  977.       
  978.     var val = null;
  979.     switch (type) {
  980.       case 0:
  981.         val = "";
  982.         break;
  983.       case 32:
  984.       default:
  985.         val = "Char";
  986.         break;
  987.       case 64:
  988.         val = "Int";
  989.         break;
  990.       case 128:
  991.         val = "Bool";
  992.         break;
  993.     };
  994.     
  995.     return val;
  996.   },
  997.     
  998.   _evalPath: function(aNode, aExpr)
  999.   {
  1000.     var result = null;
  1001.      
  1002.     try {
  1003.       var results = this._xpath.evaluate(aExpr, aNode, this._xresolve, nsIDOMXPathResult.ANY_TYPE, null);
  1004.       result = results.iterateNext(); 
  1005.     } catch(e) {};
  1006.     
  1007.     return result;    
  1008.   },
  1009.       
  1010.   _cacheFiles: function(aFrom, aTo, aDelete)
  1011.   {
  1012.     var folder = this._manager.disk.get("", ffIDisk.TYPE_CACHE);
  1013.     var i, to, name;
  1014.     var fName = "";
  1015.     var tName = "";
  1016.     
  1017.     //get names  
  1018.     fName = aFrom.getAttribute("id");
  1019.     if (aTo)
  1020.       tName = aTo.getAttribute("id");
  1021.       
  1022.     //get a list of cache files
  1023.     var entries = folder.directoryEntries;
  1024.     var files = new Array();
  1025.     while (entries.hasMoreElements()) {
  1026.       var entry = entries.getNext();
  1027.       entry = entry.QueryInterface(Components.interfaces.nsIFile);
  1028.       name = entry.leafName;
  1029.       
  1030.       //make sure its a cache file
  1031.       if (name.indexOf("cache") == -1)
  1032.         continue;
  1033.         
  1034.       //make sure it is a from cache
  1035.       if (name.indexOf(fName) != -1)
  1036.         files.push(entry);
  1037.     };
  1038.  
  1039.     //now loop through our files    
  1040.     for (i=0; i<files.length; i++) {
  1041.       
  1042.       //get to file
  1043.       if (aTo) {
  1044.         to = folder.clone();
  1045.         name = files[i].leafName;
  1046.         name = name.replace(fName, tName);       
  1047.         to.append(name);
  1048.         if (files[i].exists()) {
  1049.           if (aDelete)
  1050.             this._manager.disk.move(files[i], to);
  1051.           else
  1052.             this._manager.disk.copy(files[i], to);
  1053.         }
  1054.       };
  1055.     }  
  1056.   },
  1057.   
  1058.   _cachePrefs: function(aFrom, aTo)
  1059.   {
  1060.     //get a list of preferences
  1061.     var nodes = new Object();
  1062.     
  1063.     //loop through prefs finding cache prefs
  1064.     for (var i=0; i<this._prefs.length; i++) {
  1065.       if (this._prefs[i].indexOf("cache") == -1)
  1066.         continue;
  1067.       
  1068.       //get profile node that matches
  1069.       nodes[this._prefs[i]] = this._evalPath(aTo, "./pref[@name='" + this._prefs[i] + "']");
  1070.     };
  1071.  
  1072.     //replace value and update
  1073.     //pref may not exist if we migrated so wrap in try catch
  1074.     var fName = aFrom.getAttribute("id");
  1075.     var tName = aTo.getAttribute("id");
  1076.     for (j in nodes) {
  1077.       try {
  1078.         var value = nodes[j].getAttribute("value");
  1079.         value = value.replace(fName, tName);
  1080.         nodes[j].setAttribute("value", value);
  1081.       } catch (e) {};
  1082.     }
  1083.   },
  1084.   
  1085.   _getPrefs: function()
  1086.   {
  1087.     var excludes = new Object();
  1088.     excludes["migrated"] = true;
  1089.     excludes["pinged"] = true;
  1090.     excludes["icons.version"] = true;
  1091.     excludes["schedule.bump"] = true;
  1092.     excludes["schedule.span"] = true;     
  1093.     excludes["profile.current"] = true;
  1094.     excludes["icons.uninstallfiles"] = true;
  1095.     var defaults = this._default.getChildList("", {});                
  1096.     var prefs = new Array();
  1097.     
  1098.     for (var i=0; i<defaults.length; i++) {
  1099.       if (excludes[defaults[i]])
  1100.         continue;
  1101.       prefs.push(defaults[i]);
  1102.     };
  1103.     
  1104.     return prefs;
  1105.   },
  1106.  
  1107.   _stripName: function(name)
  1108.   {
  1109.     // remove illegal characters
  1110.     return name.replace(/[\\\/:*?<>|\'\"]/g, "");
  1111.   },
  1112.   
  1113.   ///////////////////////////
  1114.   // nsITimerCallback
  1115.   notify: function(timer)
  1116.   {
  1117.     var profiles = this._evalPath(this._doc, "//profiles");  
  1118.     profiles = profiles.getElementsByTagName("profile");
  1119.     
  1120.     // go to next profile in the list
  1121.     for (var x=0; x<profiles.length; x++) {
  1122.       if (profiles[x].getAttribute("id") == this._current) {
  1123.         this._manager.setUTFPref("profile.current", profiles[( x+1 < profiles.length ) ? x+1 : 0 ].getAttribute("id"));
  1124.         break;
  1125.       };
  1126.     };
  1127.     
  1128.     //schedule rotation
  1129.     this._schedule();
  1130.   },
  1131.      
  1132.   ///////////////////////////
  1133.   // nsIClassInfo  
  1134.   getInterfaces: function(aCount)
  1135.   {
  1136.     var ifaces = new Array();
  1137.     ifaces.push(Components.interfaces.ffIProfiles);
  1138.     ifaces.push(Components.interfaces.nsITimerCallback);
  1139.     ifaces.push(Components.interfaces.nsIClassInfo);
  1140.     ifaces.push(Components.interfaces.nsISupports);
  1141.     aCount.value = ifaces.length;
  1142.     return ifaces;
  1143.   },
  1144.   
  1145.   getHelperForLanguage: function(aLanguage) { return null; },
  1146.   get contractID() { return CONTRACT_ID; },
  1147.   get classID() { return CLASS_ID; },
  1148.   get classDescription() { return CLASS_NAME; },
  1149.   get implementationLanguage() { return Components.interfaces.nsIProgrammingLanguage.JAVASCRIPT; },
  1150.   get flags() { return Components.interfaces.nsIClassInfo.SINGLETON; },
  1151.          
  1152.   ///////////////////////////
  1153.   // nsISupports
  1154.   QueryInterface: function (aIID)
  1155.   {
  1156.     if (!aIID.equals(Components.interfaces.ffIProfiles) &&
  1157.         !aIID.equals(Components.interfaces.nsITimerCallback) &&      
  1158.         !aIID.equals(Components.interfaces.nsIClassInfo) &&      
  1159.         !aIID.equals(Components.interfaces.nsISupports))
  1160.       throw Components.results.NS_ERROR_NO_INTERFACE;
  1161.     return this;
  1162.   }
  1163. };
  1164.  
  1165. /******************************************************************************
  1166.  * XPCOM Functions for construction and registration
  1167.  ******************************************************************************/
  1168. var gModule = {
  1169.   _firstTime: true,
  1170.   registerSelf: function(aCompMgr, aFileSpec, aLocation, aType)
  1171.   {
  1172.     if (this._firstTime) {
  1173.       this._firstTime = false;
  1174.       throw Components.results.NS_ERROR_FACTORY_REGISTER_AGAIN;
  1175.     };
  1176.     aCompMgr = aCompMgr.QueryInterface(Components.interfaces.nsIComponentRegistrar);
  1177.     aCompMgr.registerFactoryLocation(CLASS_ID, CLASS_NAME, CONTRACT_ID, aFileSpec, aLocation, aType);
  1178.   },
  1179.  
  1180.   unregisterSelf: function(aCompMgr, aLocation, aType)
  1181.   {
  1182.     aCompMgr = aCompMgr.QueryInterface(Components.interfaces.nsIComponentRegistrar);
  1183.     aCompMgr.unregisterFactoryLocation(CLASS_ID, aLocation);        
  1184.   },
  1185.   
  1186.   getClassObject: function(aCompMgr, aCID, aIID)
  1187.   {
  1188.     if (!aIID.equals(Components.interfaces.nsIFactory))
  1189.       throw Components.results.NS_ERROR_NOT_IMPLEMENTED;
  1190.  
  1191.     if (aCID.equals(CLASS_ID))
  1192.       return gFactory;
  1193.  
  1194.     throw Components.results.NS_ERROR_NO_INTERFACE;
  1195.   },
  1196.  
  1197.   canUnload: function(aCompMgr) { return true; }
  1198. };
  1199.  
  1200. var gFactory = {
  1201.   createInstance: function (aOuter, aIID)
  1202.   {
  1203.     if (aOuter != null)
  1204.       throw Components.results.NS_ERROR_NO_AGGREGATION;
  1205.     return (new ffProfiles()).QueryInterface(aIID);
  1206.   }
  1207. };
  1208.  
  1209. function NSGetModule(aCompMgr, aFileSpec) { return gModule; }